use quote::quote;
use syn::{FnArg, Pat, Signature, Type, TypeParamBound};

/// 方法对应的参数信息
pub struct RpcParam {
    pub fields: Vec<proc_macro2::TokenStream>,           // param: paramType
    pub mut_params: Vec<proc_macro2::TokenStream>,       // &mut param
    pub params: Vec<proc_macro2::TokenStream>,           // param 参数名称
    pub ref_params: Vec<proc_macro2::TokenStream>,       // param 参数名称
    pub param_types: Vec<proc_macro2::TokenStream>,      // paramType

    pub has_context: bool,                               // 是否有RpcContext
}

pub fn parse_param(sig: &Signature) -> RpcParam {
    let mut params = Vec::new();
    let mut ref_params = Vec::new();
    let mut fields = Vec::new();
    let mut mut_params = Vec::new();
    let mut param_types = Vec::new();
    let mut has_context: bool = false;

    for arg in sig.inputs.iter() {
        if let FnArg::Typed(pat_type) = arg {
            if let Pat::Ident(pat_ident) = &*pat_type.pat {
                let arg_name = &pat_ident.ident;
                match &*pat_type.ty {
                    Type::Path(type_path) => {
                        if let Some(seg) = type_path.path.segments.last() {
                            let arg_type = &seg.ident;
                            fields.push(quote! { pub #arg_name: #arg_type });
                            params.push(quote! { #arg_name });
                            ref_params.push(quote! { &#arg_name });
                            mut_params.push(quote! { self.#arg_name.clone() });
                            param_types.push(quote! { #arg_type });
                        }
                    }
                    Type::Reference(reference) => {
                        match &*reference.elem {
                            Type::TraitObject(trait_object) => {
                                if let Some(TypeParamBound::Trait(trait_bound)) = trait_object.bounds.last() {
                                    if trait_bound.path.is_ident("RpcContext") {
                                        has_context = true;
                                    } else {
                                        if let Some(seg) = trait_bound.path.segments.last() {
                                            let arg_type = &seg.ident;
                                            fields.push(quote! { pub #arg_name: #arg_type });
                                            params.push(quote! { #arg_name });
                                            ref_params.push(quote! { &#arg_name });
                                            mut_params.push(quote! { &mut self.#arg_name });
                                            param_types.push(quote! { #arg_type });
                                        }
                                    }
                                }
                            }
                            Type::Path(type_path) => {
                                if let Some(seg) = type_path.path.segments.last() {
                                    let arg_type = &seg.ident;
                                    if arg_type.to_string().contains("RpcContext") {
                                        has_context = true;
                                    } else {
                                        fields.push(quote! { pub #arg_name: #arg_type });
                                        params.push(quote! { #arg_name });
                                        ref_params.push(quote! { &#arg_name });
                                        mut_params.push(quote! { &mut self.#arg_name });
                                        param_types.push(quote! { #arg_type });
                                    }
                                }
                            }
                            _ => {

                            }
                        }
                    }
                    Type::TraitObject(trait_object) => {
                        if let Some(TypeParamBound::Trait(trait_bound)) = trait_object.bounds.last() {
                            if let Some(seg) = trait_bound.path.segments.last() {
                                let arg_type = &seg.ident;
                                if arg_type.to_string().contains("RpcContext") {
                                    has_context = true;
                                } else {
                                    fields.push(quote! { pub #arg_name: #arg_type });
                                    params.push(quote! { #arg_name });
                                    ref_params.push(quote! { &#arg_name });
                                    mut_params.push(quote! { &mut self.#arg_name });
                                    param_types.push(quote! { #arg_type });
                                }
                            }
                        }
                    }
                    Type::ImplTrait(impl_trait) => {
                        if let Some(TypeParamBound::Trait(trait_bound)) = impl_trait.bounds.last() {
                            if let Some(seg) = trait_bound.path.segments.last() {
                                let arg_type = &seg.ident;
                                if trait_bound.path.is_ident("RpcContext") {
                                    has_context = true;
                                }
                                if arg_type.to_string().contains("RpcContext") {
                                    has_context = true;
                                } else {
                                    fields.push(quote! { pub #arg_name: #arg_type });
                                    params.push(quote! { #arg_name });
                                    ref_params.push(quote! { &#arg_name });
                                    mut_params.push(quote! { &mut self.#arg_name });
                                    param_types.push(quote! { #arg_type });
                                }
                            }
                        }
                    }
                    _ => {
                    }
                };

            }
        }
    }
    RpcParam {
        fields,           // param: paramType
        mut_params,       // &mut params
        params,           // params
        ref_params,       // ref params
        param_types,      // paramType
        has_context,
    }
}

fn vec_to_tuple2(vec: &Vec<&str>) -> proc_macro2::TokenStream {
    let v1 = vec[0].to_string();
    let v2 = vec[1].to_string();
    quote! {(#v1, #v2) }
}




